/****************************************************************************
*
*   DllMain.cpp
*   StickyIME DLL Hook
*
*   By Cliff Spradlin (2/12/2012)
*
***/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

/****************************************************************************
*
*   Constants
*
***/

static const wchar_t SHARED_FILENAME[] = L"StickyIMESharedMemory";


/****************************************************************************
*
*   Declarations
*
***/

// In SharedMemory, only use types that are
// safe between 32-bit and 64-bit Windows
#pragma pack(push, 1)
struct SharedMemory {
    DWORD   conversionStatus;
    DWORD   sentenceStatus;
    HKL     keyboardLayout;
};
#pragma pack(pop)


/****************************************************************************
*
*   Statics
*
***/

static SharedMemory * s_sharedMemory;
static HANDLE         s_mapFile;


/****************************************************************************
*
*   Private functions
*
***/

//===========================================================================
static void CreateSharedMemory () {
    s_mapFile = CreateFileMapping(
        INVALID_HANDLE_VALUE,    // use paging file
        NULL,                    // default security
        PAGE_READWRITE,          // read/write access
        0,                       // maximum object size (high-order DWORD)
        sizeof(SharedMemory),    // maximum object size (low-order DWORD)
        SHARED_FILENAME          // name of mapping object
    );

    bool creatingFile = GetLastError() != ERROR_ALREADY_EXISTS;

    s_sharedMemory = (SharedMemory *)MapViewOfFile(
        s_mapFile,
        FILE_MAP_ALL_ACCESS,
        0, // file offset (high-order DWORD)
        0, // file offset (low-order DWORD)
        0  // bytes to map (0 is all)
    );

    if (creatingFile) {
        // initialize memory
        s_sharedMemory->conversionStatus = 0;
        s_sharedMemory->sentenceStatus = 8;
        s_sharedMemory->keyboardLayout = GetKeyboardLayout(0);
    }
}

//===========================================================================
static void DeleteSharedMemory () {
    UnmapViewOfFile(s_sharedMemory);
    CloseHandle(s_mapFile);
    s_sharedMemory = NULL;
    s_mapFile = NULL;
}

/****************************************************************************
*
*   Exports
*
***/

// extern C to undecorate exports
extern "C" {

//===========================================================================
LRESULT CALLBACK CallWndProc (
    int nCode,
    WPARAM wParam,
    LPARAM lParam
) {
    if (nCode < 0)
        return CallNextHookEx(0, nCode, wParam, lParam);

    CWPSTRUCT * msg = (CWPSTRUCT *)lParam;
    
    if (msg->message == WM_IME_SETCONTEXT && msg->wParam == 1) {
        // focus is set on IME, switch to global conversion mode
       
        if (GetKeyboardLayout(0) != s_sharedMemory->keyboardLayout) {
            ActivateKeyboardLayout(
                s_sharedMemory->keyboardLayout, 
                KLF_SETFORPROCESS
            );
        }
        
        HIMC context = ImmGetContext(msg->hwnd);
        ImmSetConversionStatus(
            context, 
            s_sharedMemory->conversionStatus, 
            s_sharedMemory->sentenceStatus
        );
        ImmReleaseContext(msg->hwnd, context);
    }
    
    if (msg->message == WM_IME_NOTIFY) {
        // user changed local conversion mode, record it to global
        switch (msg->wParam) {
            case IMN_SETCONVERSIONMODE:
            case IMN_SETSENTENCEMODE:
            {
                HIMC context = ImmGetContext(msg->hwnd);
                ImmGetConversionStatus(
                    context, 
                    &s_sharedMemory->conversionStatus, 
                    &s_sharedMemory->sentenceStatus
                );
                ImmReleaseContext(msg->hwnd, context);
                break;
            }
        }
    }

    if (msg->message == WM_INPUTLANGCHANGE) {
        s_sharedMemory->keyboardLayout = GetKeyboardLayout(0);
    }
    
    return CallNextHookEx(0, nCode, wParam, lParam);
}

} // extern "C"

//===========================================================================
BOOL APIENTRY DllMain ( 
    HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
) {
	switch (ul_reason_for_call) {
	    case DLL_PROCESS_ATTACH:
            CreateSharedMemory();
            break;
	    case DLL_THREAD_ATTACH:
            break;
	    case DLL_THREAD_DETACH:
            break;
	    case DLL_PROCESS_DETACH:
            DeleteSharedMemory();
		    break;
	}
	return TRUE;
}
